package com.hp.activiti.service;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.transaction.Transactional;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Gateway;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hp.activiti.bean.HistoryData;
import com.hp.activiti.bean.Process;
import com.hp.activiti.bean.Result;
import com.hp.activiti.model.Approve;
import com.hp.activiti.model.RoleInfo;
import com.hp.activiti.model.UserInfo;
import com.hp.activiti.repository.ApproveRepository;


@Service
public class ApproveService {
	
	private final static Logger log = LoggerFactory.getLogger(ApproveService.class);
	
	@Autowired
	private ApproveRepository approveRep;
	
	@Autowired
	private RepositoryService rep;
	@Autowired
	private RuntimeService runservice;

	@Autowired
	private IdentityService identityservice;
	
	@Autowired
	private TaskService taskservice;
	@Autowired
	private HistoryService histiryservice;

	public List<Process> findAll() {
		List<Process> processList = new ArrayList<Process>();
		List<ProcessDefinition> list = rep.createProcessDefinitionQuery().list();
		list.forEach(action->{
			Process p = new Process();
			p.setDeploymentId(action.getDeploymentId());
			p.setId(action.getId());
			p.setKey(action.getKey());
			p.setName(action.getName());
			p.setResourceName(action.getResourceName());
			p.setDiagramresourcename(action.getDiagramResourceName());
			processList.add(p);
		});
		
		return processList;
	}

	
	@Transactional
	public Result startApprove(Approve appr, UserInfo user) {
		
		//保存流程信息
		appr.setUserId(user.getAccount());
		appr = approveRep.save(appr);
		
		//获取流程定义文件
		//使用leaveapply表的主键作为businesskey,连接业务数据和流程数据
		String buinessKey = String.valueOf(appr.getId());
		
		
		Map<String,Object> variables=new HashMap<String, Object>();
	    variables.put("applyuserid", user.getAccount());
		
		//设置用户发起人
		identityservice.setAuthenticatedUserId(user.getAccount());
		//将申请的流程主键作为buinessKey来抵用流程
		//将流程实例的id保存到请假流程业务表中 相互关联
		ProcessInstance pi = runservice.startProcessInstanceByKey("approve_process", buinessKey,variables);
		appr.setProcessInstanceId(pi.getId());
		approveRep.save(appr);
		return Result.success();
	}


	/**
	 * @Description:
	 * @date 2019年6月14日 上午10:35:33
	 */
	@Transactional
	public Result complateApproval(String processDefinitionId, String taskDefinitionKey,String taskId, String approFlag, UserInfo user) {
		//任务签收
		taskservice.claim(taskId, user.getAccount());
		
		//任务完成详情
		Map<String,Object> variables=new HashMap<String,Object>();
		//根据用户判断用户角色 ，从而判断节点 标识
		String key = queryConditionsKey(processDefinitionId, taskDefinitionKey);
		variables.put(key, approFlag);
		
		taskservice.complete(taskId, variables);
		
		return Result.success();
	}


	/*
	 * 上传流程
	 */
	public Result uploadworkflow(MultipartFile uploadfile) {
		try{
			MultipartFile file=uploadfile;
			String filename=file.getOriginalFilename();
			InputStream is=file.getInputStream();
			rep.createDeployment().addInputStream(filename, is).deploy();
		}catch(Exception e){
			e.printStackTrace();
			return Result.fail("上传流程信息失败!");
		}
		return Result.success();
	}

	public List<Approve> queryPendingApproval(UserInfo user) {
		List<Approve> list = new ArrayList<Approve>();
		if(StringUtils.isEmpty(user)){
			throw new RuntimeException("请重新登录!");
		}
		
		List<RoleInfo> roleList = user.getRoleList();
		List<String> roleStr = roleList.stream().map(RoleInfo::getRoleCode).collect(Collectors.toList());
		List<Task> taskList = taskservice.createTaskQuery().taskCandidateGroupIn(roleStr).list();
		if(!CollectionUtils.isEmpty(taskList)){
			for(Task tk : taskList){
				String prcoessId = tk.getProcessInstanceId();	
				ProcessInstance process = runservice.createProcessInstanceQuery().processInstanceId(prcoessId).singleResult();
				Long bizKey = Long.parseLong(process.getBusinessKey());
				Approve app = approveRep.findById(bizKey).get();
				app.setTaskId(tk.getId());
				app.setProcessDefinitionId(tk.getProcessDefinitionId());
				app.setTaskDefinitionKey(tk.getTaskDefinitionKey());
				list.add(app);
			}
		}
		return list;
	}
	
	
	/**
	 * 动态获取审批节点的判断条件
	 * @Description:
	 * @date 2019年6月20日 下午5:34:02
	 */
	private String queryConditionsKey(String processDefinitionId, String taskDefinitionKey){
		String conditionKey = "";
		BpmnModel bpmnModel = rep.getBpmnModel(processDefinitionId);
		
		//获取当前用户任务 
		UserTask flow = (UserTask) bpmnModel.getFlowElement(taskDefinitionKey);
		log.info("flow"+JSONObject.toJSONString(flow));
		//获取当前节点下级连线
		List<SequenceFlow> seqFlow = flow.getOutgoingFlows();
		for(SequenceFlow seq : seqFlow){
			//获取指向网关信息
			Gateway getway = (Gateway) seq.getTargetFlowElement();
			//获取指向网关的下级连线
			List<SequenceFlow> seqc = getway.getOutgoingFlows();
			//获取判断条件
			conditionKey =  seqc.get(0).getConditionExpression();
			//${beAproFlag=="Y"}
			int row = conditionKey.indexOf("==");
			conditionKey = conditionKey.substring(2,row);
			break;
		}
		
		return conditionKey;
	}


	/**
	 * @Description:获取当前用户已申请的信息
	 * @date 2019年6月21日 上午9:35:55
	 */
	public List<Approve> queryApplyData(UserInfo user) {
		List<Approve> list = approveRep.findByUserId(user.getAccount());
		list.forEach(action->{
			ProcessInstance prcess = runservice.createProcessInstanceQuery().processInstanceId(action.getProcessInstanceId()).singleResult();
			action.setZt(false);
			if(prcess == null ){
				action.setZt(true);
			}
		});
		return list;
	}


	/**
	 * @Description:获取审批历史
	 * @date 2019年6月21日 上午10:35:13
	 */
	public List<HistoryData> queryHistoryTask(String processInstanceId) {
		List<HistoryData> list = new ArrayList<HistoryData>();
		HistoricProcessInstance hip = histiryservice.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
		List<HistoricActivityInstance> haiList = histiryservice.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
		haiList.forEach(action->{
			if(action.getActivityType().equals("endEvent") || action.getActivityType().equals("startEvent") || !StringUtils.isEmpty(action.getTaskId())){
				HistoryData hd = new HistoryData();
				hd.setActivityName(action.getActivityName());
				if(action.getActivityId().equals("startEvent")){
					hd.setUserId(hip.getStartUserId());
				}else{
					hd.setUserId(action.getAssignee());
				}
				//通过主键id获取审批的结果
				if(!StringUtils.isEmpty(action.getTaskId())) {
					HistoricDetail hisDetail = histiryservice.createHistoricDetailQuery().activityInstanceId(action.getId()).singleResult();
					@SuppressWarnings("unchecked")
					Map<String,Object> map = JSON.parseObject(JSONObject.toJSONString(hisDetail), Map.class);
					hd.setApprovalFlag(map.get("value").toString().equals("Y")?true:false);
				}
				hd.setEndTime(action.getEndTime());
				hd.setStartTime(action.getStartTime());
				hd.setTaskId(action.getTaskId());
				list.add(hd);
			}
		});
		
		return list;
	}


	/**
	 * @Description:调整申请
	 * @date 2019年6月21日 下午5:12:51
	 */
	public List<Approve> queryChangeApproval(UserInfo user) {
		List<Approve> list = new ArrayList<Approve>();
		if(StringUtils.isEmpty(user)){
			throw new RuntimeException("请重新登录!");
		}
		
		List<Task> taskList = taskservice.createTaskQuery().taskName("调整申请").taskAssignee(user.getAccount()).list();
		if(!CollectionUtils.isEmpty(taskList)){
			for(Task tk : taskList){
				String prcoessId = tk.getProcessInstanceId();	
				ProcessInstance process = runservice.createProcessInstanceQuery().processInstanceId(prcoessId).singleResult();
				Long bizKey = Long.parseLong(process.getBusinessKey());
				Approve app = approveRep.findById(bizKey).get();
				app.setTaskId(tk.getId());
				app.setProcessDefinitionId(tk.getProcessDefinitionId());
				app.setTaskDefinitionKey(tk.getTaskDefinitionKey());
				list.add(app);
			}
		}
		return list;
	}


	/**
	 * @Description:
	 * @date 2019年6月21日 下午5:29:59
	 */
	@Transactional
	public Result complateChangeApply(String approFlag,Approve appr) {
		//获取任务task
		approveRep.save(appr);
		//任务完成详情
		Map<String,Object> variables=new HashMap<String,Object>();
		//根据用户判断用户角色 ，从而判断节点 标识
		String key = queryConditionsKey(appr.getProcessDefinitionId(), appr.getTaskDefinitionKey());
		variables.put(key, approFlag);
		
		taskservice.complete(appr.getTaskId(), variables);
		return Result.success();
	}
    
    /**
	 * 获取已流经的流程线，需要高亮显示高亮流程已发生流转的线id集合
	 * 
	 * @param bpmnModel
	 * @param historicActivityInstanceList
	 * @return
	 */
	public List<String> getHighLightedFlows(BpmnModel bpmnModel,
			List<HistoricActivityInstance> historicActivityInstanceList) {
		// 已流经的流程线，需要高亮显示
		List<String> highLightedFlowIdList = new ArrayList<>();
		// 全部活动节点
		List<FlowNode> allHistoricActivityNodeList = new ArrayList<>();
		// 已完成的历史活动节点
		List<HistoricActivityInstance> finishedActivityInstanceList = new ArrayList<>();

		for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
			// 获取流程节点
			FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance
					.getActivityId(), true);
			allHistoricActivityNodeList.add(flowNode);
			// 结束时间不为空，当前节点则已经完成
			if (historicActivityInstance.getEndTime() != null) {
				finishedActivityInstanceList.add(historicActivityInstance);
			}
		}

		FlowNode currentFlowNode = null;
		FlowNode targetFlowNode = null;
		HistoricActivityInstance currentActivityInstance;
		// 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
		for (int k = 0; k < finishedActivityInstanceList.size(); k++) {
			currentActivityInstance = finishedActivityInstanceList.get(k);
			// 获得当前活动对应的节点信息及outgoingFlows信息
			currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance
					.getActivityId(), true);
			// 当前节点的所有流出线
			List<SequenceFlow> outgoingFlowList = currentFlowNode.getOutgoingFlows();

			/**
			 * 遍历outgoingFlows并找到已流转的 满足如下条件认为已流转： 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
			 * 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转 (第2点有问题，有过驳回的，会只绘制驳回的流程线，通过走向下一级的流程线没有高亮显示)
			 */
			if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(
					currentActivityInstance.getActivityType())) {
				// 遍历历史活动节点，找到匹配流程目标节点的
				for (SequenceFlow outgoingFlow : outgoingFlowList) {
					// 获取当前节点流程线对应的下级节点
					targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(outgoingFlow.getTargetRef(),
							true);
					// 如果下级节点包含在所有历史节点中，则将当前节点的流出线高亮显示
					if (allHistoricActivityNodeList.contains(targetFlowNode)) {
						highLightedFlowIdList.add(outgoingFlow.getId());
					}
				}
			} else {
				/**
				 * 2、当前节点不是并行网关或兼容网关 【已解决-问题】如果当前节点有驳回功能，驳回到申请节点，则因为申请节点在历史节点中，导致当前节点驳回到申请节点的流程线被高亮显示，但实际并没有进行驳回操作
				 */
				// 当前节点ID
				String currentActivityId = currentActivityInstance.getActivityId();
				boolean ifStartFind = false;
				boolean ifFinded = false;
				HistoricActivityInstance historicActivityInstance;
				// 循环当前节点的所有流出线
				// 循环所有历史节点
				for (int i = 0; i < historicActivityInstanceList.size(); i++) {
					// // 如果当前节点流程线对应的下级节点在历史节点中，则该条流程线进行高亮显示（【问题】有驳回流程线时，即使没有进行驳回操作，因为申请节点在历史节点中，也会将驳回流程线高亮显示-_-||）
					// if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
					// Map<String, Object> map = new HashMap<>();
					// map.put("highLightedFlowId", sequenceFlow.getId());
					// map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
					// tempMapList.add(map);
					// // highLightedFlowIdList.add(sequenceFlow.getId());
					// }
					// 历史节点
					historicActivityInstance = historicActivityInstanceList.get(i);
					// 如果循环历史节点中的id等于当前节点id，从当前历史节点继续先后查找是否有当前节点流程线等于的节点
					// 历史节点的序号需要大于等于已完成历史节点的序号，防止驳回重审一个节点经过两次是只取第一次的流出线高亮显示，第二次的不显示
					if ( i>=k && historicActivityInstance.getActivityId().equals(currentActivityId)) {
						ifStartFind = true;
						// 跳过当前节点继续查找下一个节点
						continue;
					}
					if (ifStartFind) {

						ifFinded = false;
						for (SequenceFlow sequenceFlow : outgoingFlowList) {
							// 如果当前节点流程线对应的下级节点在其后面的历史节点中，则该条流程线进行高亮显示
							// 【问题】
							if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
								highLightedFlowIdList.add(sequenceFlow.getId());
								ifFinded = true;
								break;
							}
						}
					}
					if (ifFinded) {
						// 暂时默认找到离当前节点最近的下一级节点即退出历史节点循环，否则有多条流出线时将全部被高亮显示
						break;
					}
				}

			}

		}
		return highLightedFlowIdList;
	}

	/**
	 * 根据流程实例Id,获取实时流程图片
	 * 
	 * @param procInstId
	 * @param outputStream
	 * @return
	 * @throws Exception
	 */
	public byte[] getFlowImgByProcInstId(String procInstId) throws Exception {
		if (StringUtils.isEmpty(procInstId)) {
			throw new Exception("[异常]-传入的参数procInstId为空！");
		}
		try {
			// 通过流程实例ID获取历史流程实例
			HistoricProcessInstance historicProcessInstance = histiryservice.createHistoricProcessInstanceQuery().processInstanceId(procInstId).singleResult();
			// 通过流程实例ID获取流程中已经执行的节点，按照执行先后顺序排序
			List<HistoricActivityInstance> historicActivityInstanceList = histiryservice.createHistoricActivityInstanceQuery().processInstanceId(procInstId).orderByHistoricActivityInstanceStartTime().asc().list();

			// 将已经执行的节点ID放入高亮显示节点集合
			List<String> highLightedActivitiIdList = new ArrayList<String>();
			for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
				highLightedActivitiIdList.add(historicActivityInstance.getActivityId());
			}

			// 通过流程实例ID获取流程中正在执行的节点
			List<Execution> runningActivityInstanceList = runservice.createExecutionQuery().processInstanceId(procInstId).list();
			List<String> runningActivitiIdList = new ArrayList<String>();
			for (Execution execution : runningActivityInstanceList) {
				if (!StringUtils.isEmpty(execution.getActivityId())) {
					runningActivitiIdList.add(execution.getActivityId());
				}
			}

			// 定义流程画布生成器
			ProcessDiagramGenerator processDiagramGenerator = null;
			// 如果还没完成，流程图高亮颜色为绿色，如果已经完成为红色
			// if (!CollectionUtils.isEmpty(historicFinishedProcessInstanceList)) {
			// // 如果不为空，说明已经完成
			// processDiagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
			// } else {
			processDiagramGenerator = new CustomProcessDiagramGenerator();
			// }

			// 获取流程定义Model对象
			BpmnModel bpmnModel = rep.getBpmnModel(historicProcessInstance.getProcessDefinitionId());

			// 获取已流经的流程线，需要高亮显示高亮流程已发生流转的线id集合
			List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel,
					historicActivityInstanceList);

			// 使用默认配置获得流程图表生成器，并生成追踪图片字符流
			InputStream imageStream = ((CustomProcessDiagramGenerator) processDiagramGenerator).generateDiagramCustom(
					bpmnModel, "png", highLightedActivitiIdList, runningActivitiIdList, highLightedFlowIds, "宋体",
					"微软雅黑", "黑体", null, 2.0);

			// 将InputStream数据流转换为byte[]
			byte[] buffer = new byte[imageStream.available()];
            imageStream.read(buffer);
            imageStream.close();
			return buffer;
		} catch (Exception e) {
			throw new Exception("通过流程实例ID" + procInstId + "获取流程图时出现异常！", e);
		}

	}
	
	
	
}
